package com.aim.analysis.records;

import com.aim.game.MC;
import com.aim.util.HashMapOfList;
import com.aim.util.MeanVarPair;
import com.aim.util.ObjectCounter;
import com.aim.util.StatData;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;

import java.util.*;
import java.util.logging.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: User
 * Date: 6/4/14
 * Time: 10:47 PM
 * To change this template use File | Settings | File Templates.
 */
public class Game {

    static Logger logger = Logger.getLogger(Game.class.getCanonicalName());


    private GameKey key;
    public long[] shutdownTimes = {0};
    public long[] collisionTimes = {0};

    public long[] fireTimes = {0};
    public int totalShutdown;
    public int totalCollision;

    public int totalFire;

    private HashMapOfList<RecordType, GameRecord> recordsMap = new HashMapOfList<RecordType, GameRecord>();
    private TreeSet<GameRecord> orderedRecords = new TreeSet<GameRecord>(new Comparator<GameRecord>() {
        @Override
        public int compare(GameRecord o1, GameRecord o2) {
            return o1.sequenceNumber - o2.sequenceNumber;
        }
    });
    private boolean isValid = true;

    //PROCEEDED DATA

    //maintenance
    public int maintenanceTime;
    public int maintenanceCost;

    ObjectCounter<MC> repairCounter = new ObjectCounter<MC>();
    public int repairTime = 0;
    public int repairCost = 0;

    public int stationRefuelCost = 0;
    public int spaceshipRefuelCost = 0;
    public int totalRefuelCost = 0;

    public int refuelTime = 0;
    public int freezeTime = 0;

    public int netTime = 0;
    public double netScore = 0;

    public MeanVarPair maintenanceAfterAlertDelay;
    public MeanVarPair malfunctionAfterAlertDelay;
    public int numOfMaintenanceAfterAlert;
    public int numOfMalfunctionAfterAlert;

    public StatData<Integer> recommendedTimeForAlert;
    public StatData<Integer> responseDelay;
    public int numOfAlerts;




    public int gameLength;
    public int calculatedScore = 0;
    public ArrayList<Integer> responseDelayData;

    public double avgMSE;
    public double segmentMSE;

    public Game(GameKey key) {
        this.key = key;
    }

    public GameKey getKey() {
        return key;
    }


    public void addRecord(GameRecord record) {
        orderedRecords.add(record);

    }

    public int getFinalScore() {
        return lastRecord().score;
    }

    public int numOfRecords(RecordType recordType) {
        List<GameRecord> records = getRecords(recordType);
        return records == null ? 0 : records.size();
    }

    private GameRecord lastRecord() {
        return orderedRecords.last();
    }

    public Set<GameRecord> getRecords() {
        return Collections.unmodifiableSet(orderedRecords);
    }

    public List<GameRecord> getRecords(RecordType recordType) {
        List<GameRecord> recordList = recordsMap.get(recordType);
        return recordList != null? recordList : new ArrayList<GameRecord>();
    }

    public int[] getTimes(RecordType recordType) {
        List<GameRecord> records = getRecords(recordType);
        return getTimes(records);
    }

    public int[] getRepairTimes(MC mc) {
        List<GameRecord> records = getRecords(RecordType.repairAction);
        List<GameRecord> filteredRecords = new LinkedList<GameRecord>();
        for (GameRecord record : records) {
            if (record.mechanicalCondition == mc) {
                filteredRecords.add(record);
            }
        }
        return getTimes(filteredRecords);
    }

    private int[] getTimes(List<GameRecord> records) {
        List<Integer> timeList = new LinkedList<Integer>();
        if (records != null) {
            for (GameRecord record : records) {
                timeList.add(record.time);
            }
        }
        return Ints.toArray(timeList);
    }

    public void postProcessing() {
        validate();

        PerformanceDataCollector performanceDataCollector = new PerformanceDataCollector();
        for (GameRecord record : orderedRecords) {
            recordsMap.put(record.recordType, record);
            if (record.recordType == RecordType.meteorSummery) {
                performanceDataCollector.addPerformanceData(record.getPerformanceData());
            }
        }
        collisionTimes = performanceDataCollector.getCollisionTimesArray();
        shutdownTimes = performanceDataCollector.getShutdownTimesArray();
        fireTimes = performanceDataCollector.getFireTimesArray();
        totalCollision = performanceDataCollector.getTotalCollision();
        totalShutdown = performanceDataCollector.getTotalShutdown();
        totalFire = performanceDataCollector.getTotalFire();

        GameProcessor processor = new GameProcessor(this);
        processor.init();
    }



    private void validate() {
        if (orderedRecords.size() != orderedRecords.last().sequenceNumber){
            //assert false;
            String missed = getMissedMessageReport();
            logger.warning(key.toString() + "\t" + orderedRecords.size() + "\t" + "missed messages: " + missed);
        }
       // assert orderedRecords.size() == orderedRecords.last().sequenceNumber : key.toString() + "\t" + orderedRecords.size();
        if (orderedRecords.first().recordType == RecordType.gameStarted) {
            isValid = false;
        }

        if (orderedRecords.last().recordType != RecordType.gameEnd) {
            logger.warning("end message not founded (" + getKey() + ")");
            isValid = false;
        }
    }

    private String getMissedMessageReport(){
        int seqNumber = 1;
        String missed = "";
        for (GameRecord orderedRecord : orderedRecords) {
            if (orderedRecord.sequenceNumber != seqNumber){
                while (seqNumber < orderedRecord.sequenceNumber){
                    missed += "," + seqNumber;
                    seqNumber++;
                }
            }
            seqNumber++;
        }
        return missed;
    }




    public boolean isValid() {
        return isValid;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Game game = (Game) o;

        if (key != null ? !key.equals(game.key) : game.key != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return key != null ? key.hashCode() : 0;
    }

    public String getImageFileName(){
        return key.partitionKey + "_" + key.gameDescription + "_" + key.gameNumber  + ".jpg";
    }

    public int getGameLength() {
        return orderedRecords.last().time - orderedRecords.first().time;
    }


    public void setMaintenanceTime(int maintenanceTime) {
        this.maintenanceTime = maintenanceTime;
    }

    public void setMaintenanceCost(int maintenanceCost) {
        this.maintenanceCost = maintenanceCost;
    }

    public void setFreezeTime(int freezeTime) {
        this.freezeTime = freezeTime;
    }

    public int getNetTime() {
        return netTime;
    }

    public void setNetTime(int netTime) {
        this.netTime = netTime;
    }

    public double getNetScore() {
        return netScore;
    }

    public void setNetScore(double netScore) {
        this.netScore = netScore;
    }

    public int getCalculatedScore() {
        return calculatedScore;
    }

    public long[] getCollisionTimes() {
        return collisionTimes;
    }

    public long[] getShutdownTimes() {
        return shutdownTimes;
    }

    public long[] getFireTimes() {
        return fireTimes;
    }
}
